import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

/**
 * 
 */

/**
 * @author IBRAHIMLI NIhat
 * @author BAYRAMOV Matin
 * 
 */
public class Graph {

	public static final int MAX_TASK_NUMBER = 100;

	private List<Task> tasks;

	private List<Edge> edges;

	public Graph() {
		this.edges = new ArrayList<Edge>();
		this.tasks = new ArrayList<Task>();

		this.createTasks();
		this.createEdges();
	}

	/**
	 * 
	 * @param graph
	 */
	public void createTasks() {
		for (int i = 0; i < MAX_TASK_NUMBER; i++) {
			this.addTask(Task.getTask(MAX_TASK_NUMBER));
		}
	}

	/**
	 * 
	 */
	public void createEdges() {
		for (int index = 0, size = this.tasks.size(); index < size - 1; index++) {
			Task from = this.tasks.get(index);
			Task to = this.getRandomTask(index + 1, MAX_TASK_NUMBER);
			Edge edge = new Edge(from, to,
					Edge.getRandomWeight(MAX_TASK_NUMBER));
			this.addEdge(edge);
		}

		this.reTypeTask();
	}

	public void computeBottomLevel() {
		for (int i = this.getTaskSize() - 1; i >= 0; i--) {
			this.getTasks().get(i).defineBottomLevel();
		}
	}

	/**
	 * 
	 * @param task
	 * @return
	 */
	public void computeTopLevel() {
		for (int i = 0; i < this.getTaskSize(); i++) {
			this.getTasks().get(i).defineTopLevel();
		}
	}

	/**
	 * 
	 * @param task
	 */
	public void computeSuccessorsTopLevel(Task task) {
		Iterator<Entry<Task, Integer>> iter = task.getSuccessor().entrySet()
				.iterator();
		while (iter.hasNext()) {
			Map.Entry<Task, Integer> entry = (Map.Entry<Task, Integer>) iter
					.next();
			entry.getKey().defineTopLevel();
		}
	}

	public Task getTheHighestPriorityTask(List<Task> tasks) {

		int highestMakeSpan = 0;
		int makeSpan = 0;
		int highestPriorityTaskId = 0;
		
		if (tasks.size() == 0) {
			return null;
		}

		for (int i = 0, size = tasks.size(); i < size; i++) {
			makeSpan = tasks.get(i).getPriority();
			if (highestMakeSpan < makeSpan) {
				highestMakeSpan = makeSpan;
				highestPriorityTaskId = i;
			}
		}

		return tasks.get(highestPriorityTaskId);
	}

	/**
	 * Get a random task from list of tasks
	 * 
	 * @param idMin
	 * @param idMax
	 * @return
	 */
	public Task getRandomTask(int idMin, int idMax) {

		int index = idMin + (int) (Math.random() * (idMax - idMin));
		return this.tasks.get(index);
	}

	/**
	 * 
	 * @param task
	 */
	public void addTask(Task task) {
		this.tasks.add(task);
	}

	/**
	 * 
	 * @param edge
	 * @param task
	 */
	public void addTaskToSuccessor(Edge edge) {
		int index = this.tasks.indexOf(edge.getFrom());
		this.tasks.get(index).addSuccessor(edge.getTo(), edge.getWeight());
	}

	/**
	 * 
	 * @param edge
	 */
	public void addTaskToPredecessor(Edge edge) {
		int index = this.tasks.indexOf(edge.getTo());
		this.tasks.get(index).addPredecessor(edge.getFrom(), edge.getWeight());
	}

	/**
	 * 
	 */
	public void reTypeTask() {
		for (int i = 0, size = this.getEdgesSize(); i < size; i++) {
			Task to = this.edges.get(i).getTo();
			if (to.getType() == 1) {
				to.setType(2);
			}
		}
	}

	/**
	 * 
	 * @param edge
	 */
	public void addEdge(Edge edge) {
		edge.getFrom().setType(1);
		this.edges.add(edge);
		this.addTaskToPredecessor(edge);
		this.addTaskToSuccessor(edge);
	}

	/**
	 * Allows us to add an edge not added yet
	 * 
	 * @param edge
	 */
	public void addNewEdges(Edge edge) {
		if (!this.edgeExist(edge)) {
			this.addEdge(edge);
			this.addTaskToPredecessor(edge);
			this.addTaskToSuccessor(edge);
		}
	}

	/**
	 * Allows us to know whether an edge is added or not
	 * 
	 * @param edge
	 * @return
	 */
	public boolean edgeExist(Edge edge) {
		for (int i = 0, size = this.getEdgesSize(); i < size; i++) {
			if (this.edges.get(i).compareTo(edge) == 0) {
				return true;
			}
		}
		return false;
	}
	
	public int getMakeSpan() {
		
		int highestPriority = 0;
		int priority = 0;
		
		for (int i = 0, size = tasks.size(); i < size; i++) {
			priority = tasks.get(i).getPriority();			
			if (highestPriority < priority) {
				highestPriority = priority;
			}
		}
		
		return highestPriority;
	}

	/**
	 * @return the tasks
	 */
	public List<Task> getTasks() {
		return tasks;
	}

	/**
	 * @param tasks
	 *            the tasks to set
	 */
	public void setTasks(List<Task> tasks) {
		this.tasks = tasks;
	}

	/**
	 * @return the edges
	 */
	public List<Edge> getEdges() {
		return edges;
	}

	/**
	 * @param edges
	 *            the edges to set
	 */
	public void setEdges(List<Edge> edges) {
		this.edges = edges;
	}

	/**
	 * Get size of edges in the graph
	 * 
	 * @return
	 */
	public int getEdgesSize() {
		return this.edges.size();
	}

	/**
	 * Get size of task in the graph
	 * 
	 * @return
	 */
	public int getTaskSize() {
		return this.tasks.size();
	}

	/**
	 * 
	 */
	public void reGenerateGraphe() {
		int min = MAX_TASK_NUMBER - 1;
		int max = (MAX_TASK_NUMBER * min) / 2;

		int nbEdges = (int) (Math.random() * ((max - min) + 1));

		while (this.getEdgesSize() - min != nbEdges) {

			Task from = this.getRandomTask(0, MAX_TASK_NUMBER - 1);
			Task to = this.getRandomTask(from.getId(), MAX_TASK_NUMBER);
			this.addNewEdges(new Edge(from, to, Edge
					.getRandomWeight(MAX_TASK_NUMBER)));
		}

		this.reTypeTask();
	}

	public String toString() {
		String str = "";

		for (int index = 0, size = this.tasks.size(); index < size; index++) {
			str += "[ t" + this.tasks.get(index).getId() + "-("
					+ this.tasks.get(index).getExecutionTime() + "s) "
					+ this.tasks.get(index).getType() + "]: ";
			for (int i = 0, s = this.edges.size(); i < s; i++) {
				if (this.edges.get(i).getFrom().getId() == this.tasks
						.get(index).getId()) {
					str += "{ t" + this.edges.get(i).getTo().getId() + ", ";
					str += "" + this.edges.get(i).getWeight();
					str += "s }, ";
				}
			}

			str += "\n";

		}

		return str;
	}

	public String tasksBottomLevel() {
		String str = "";

		for (int index = 0, size = this.tasks.size(); index < size; index++) {
			str += "[ t" + this.tasks.get(index).getId() + "-("
					+ this.tasks.get(index).getExecutionTime() + "s) "
					+ this.tasks.get(index).getType() + "]: ";
			str += "bttm => " + this.tasks.get(index).getBottomLevel();
			str += "\n";
		}

		return str;
	}

	public String tasksTopLevel() {
		String str = "";

		for (int index = 0, size = this.tasks.size(); index < size; index++) {
			str += "[ t" + this.tasks.get(index).getId() + "-("
					+ this.tasks.get(index).getExecutionTime() + "s) "
					+ this.tasks.get(index).getType() + "]: ";
			str += "top => " + this.tasks.get(index).getTopLevel();
			str += "\n";
		}

		return str;
	}

}
